using System;
using System.Collections;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Collections.Generic;
using System.Linq;

namespace OpenWaves
{
    public class UrlQueryCollection : IEnumerable<KeyValuePair<string, string>>
    {
        public readonly static UrlQueryCollection Empty = new UrlQueryCollection();

        public static UrlQueryCollection Parse(string query)
        {
            Contract.Requires<ArgumentNullException>(query != null);

            if (query.StartsWith("?", StringComparison.Ordinal))
                query = query.Remove(0, 1);

            var variables = new Dictionary<string, string>();
            foreach (var parameter in query.Split(new []{'&'},StringSplitOptions.RemoveEmptyEntries))
            {
                var i = parameter.IndexOf('=');
                string key;
                string value;
                if (i < 0)
                {
                    key = Url.Decode(parameter);
                    value = String.Empty;
                }
                else
                {
                    key = Url.Decode(parameter.Substring(0, i));
                    value = Url.Decode(parameter.Substring(i + 1));
                }

                if (variables.ContainsKey(key) == false)
                    variables.Add(key, value);
            }

            return new UrlQueryCollection(variables);
        }

        public static UrlQueryCollection FromNameValueDictionary<T>(IEnumerable<KeyValuePair<string, T>> dictionary)
        {
            var parameters = new Dictionary<string, string>();
            foreach (var variable in dictionary)
            {
                parameters.Add(variable.Key, Convert.ToString(variable.Value, CultureInfo.InvariantCulture));
            }

            return new UrlQueryCollection(parameters);
        }

        public int Count
        {
            get { return this.variables.Count; }
        }

        private readonly IDictionary<string, string> variables;

        private UrlQueryCollection(IDictionary<string, string> variables)
        {
            Contract.Requires<ArgumentNullException>(variables != null);

            this.variables = variables;
        }

        private UrlQueryCollection(): this(new Dictionary<string, string>())
        {
        }

        public string this[string key]
        {
            get { return this.variables[key]; }
        }

        public bool ContainsKey(string key)
        {
            return this.variables.ContainsKey(key);
        }

        public bool TryGetValue(string key, out string value)
        {
            return this.variables.TryGetValue(key, out value);
        }

        public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
        {
            return this.variables.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public override string ToString()
        {
            return this.variables.OrderBy(variable=>variable.Key).Select(EncodeVariable).JoinStrings("&");
        }

        private static string EncodeVariable(KeyValuePair<string, string> variable)
        {
            if (String.IsNullOrEmpty(variable.Value))
                return Url.Encode(variable.Key);
            else
                return String.Format(CultureInfo.InvariantCulture, "{0}={1}", Url.Encode(variable.Key), Url.Encode(variable.Value));
        }
    }
}
